﻿using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Script.Serialization;
using NAnt.Core;
using NAnt.Core.Attributes;

namespace Lean.NantTasks.Deployment
{
    /// <summary>
    /// Reads a given json-file which contains an array of jsonobjects.
    /// It then executes all subtasks for each array entry exposing the jsonobjects as temporary nant-properties.
    /// Nested json-objects are flattened. Eg. 
    /// JSON:
    ///         { name: { first: "Thomas", last: "Kowalski" } }
    /// Properties:
    ///     <property name="name.first" value="Thomas" /> 
    ///     <property name="name.last" value="Kowalski" /> 
    /// </summary>
    [TaskName("jsonconfig")]
    public class JsonConfig : TaskContainer
    {
        [TaskAttribute("file"), StringValidator(AllowEmpty = false)]
        public FileInfo Fileinfo { get; set; }

        protected override void ExecuteTask()
        {
            var js = new JavaScriptSerializer();
            var json = File.ReadAllText(Fileinfo.FullName);
            var objectGraph = js.DeserializeObject(json);

            var dicts = ((object[])objectGraph).OfType<Dictionary<string, object>>();

            foreach (var dict in dicts.Select(ReduceNesting))
            {
                var backup = BackupProperties(dict);
                SetProperties(dict);
                base.ExecuteTask();
                RestoreProperties(backup);
            }
        }


        private Dictionary<string, object> ReduceNesting(Dictionary<string, object> jsobj)
        {
            var dict = new Dictionary<string, object>();
            foreach (var kvp in jsobj)
            {
                if (kvp.Value is Dictionary<string, object>)
                {
                    var val = kvp.Value as Dictionary<string, object>;
                    var reducedDict = ReduceNesting(val);
                    foreach (var kvp2 in reducedDict)
                    {
                        dict[kvp.Key + "." + kvp2.Key] = kvp2.Value;
                    }
                }
                else
                {
                    dict[kvp.Key] = kvp.Value;
                }
            }
            return dict;
        }

        private void RestoreProperties(Dictionary<string, string> backup)
        {
            foreach (var kvp in backup)
            {
                if (kvp.Value == null) {
                    Properties.Remove(kvp.Key);
                }
                else { 
                    Properties[kvp.Key] = kvp.Value; 
                }
            }
        }

        private Dictionary<string, string> BackupProperties(Dictionary<string, object> configObj)
        {
            return configObj.ToDictionary(
                  pair => pair.Key,
                  pair => (Properties.Contains(pair.Key))
                                        ? Properties[pair.Key]
                                        : null
                );
        }

        private void SetProperties(Dictionary<string, object> configObj)
        {
            foreach (var kvp in configObj)
            {
                Properties[kvp.Key] = kvp.Value.ToString();
                Properties.MarkDynamic(kvp.Key);

                //Properties.ExpandProperties(kvp.Value.ToString(), Location);
            }
        }
    }
}